package com.atguigu.ad.hive.udf;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.*;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

import java.net.URL;
import java.util.ArrayList;

/**
 * @author yhm
 * @create 2023-02-14 10:56
 */
public class ParseIP extends GenericUDF {

    private String ConstantHttpUrl ;

    // 1. 对传入参数进行校验
    // 2. 对返回值类型进行限定
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        // 判断参数个数
        if (arguments.length != 2){
            throw new UDFArgumentException("parseIP的参数必须为2个");
        }
        // 判断第一个参数为string
        ObjectInspector httpURLOI = arguments[0];
        ObjectInspector.Category category = httpURLOI.getCategory();
        if (!ObjectInspector.Category.PRIMITIVE.equals(category)){
            throw new UDFArgumentException("parseIP的第一个参数必须是基本数据类型");
        }
        PrimitiveObjectInspector httpURLOI1 = (PrimitiveObjectInspector) httpURLOI;
        if (!PrimitiveObjectInspector.PrimitiveCategory.STRING.equals(httpURLOI1.getPrimitiveCategory())){
            throw new UDFArgumentException("parseIP的第一个参数必须是string类型");
        }

        // 判断第一个参数是否为能够连接的url
        // 只有在传入参数为常量的时候能够获取值
        if (httpURLOI instanceof ConstantObjectInspector){
            String httpURL = ((ConstantObjectInspector) httpURLOI).getWritableConstantValue().toString();
            try {
                URL url = new URL(httpURL);
                url.openConnection().connect();
                ConstantHttpUrl = httpURL;
            }catch (Exception e){
                throw new UDFArgumentException("parseIP填写的http地址无法连接");
            }
        }

        // 判断第二个参数为string
        ObjectInspector ipOI = arguments[1];
        ObjectInspector.Category ipCategory = ipOI.getCategory();
        if (!ObjectInspector.Category.PRIMITIVE.equals(ipCategory)){
            throw new UDFArgumentException("parseIP的第一个参数必须是基本数据类型");
        }
        PrimitiveObjectInspector ipOI1 = (PrimitiveObjectInspector) ipOI;
        if (!PrimitiveObjectInspector.PrimitiveCategory.STRING.equals(ipOI1.getPrimitiveCategory())){
            throw new UDFArgumentException("parseIP的第一个参数必须是string类型");
        }

        ArrayList<String> structFieldNames = new ArrayList<>();
        ArrayList<ObjectInspector> structFieldOI = new ArrayList<>();

        structFieldNames.add("country");
        structFieldNames.add("province");
        structFieldNames.add("city");
        structFieldNames.add("area");
        structFieldNames.add("isp");

        structFieldOI.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        structFieldOI.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        structFieldOI.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        structFieldOI.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
        structFieldOI.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);

        return ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, structFieldOI);
    }

    // 处理数据
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
        String url = arguments[1].get().toString();

        HttpRequest httpRequest = HttpRequest.get(ConstantHttpUrl);
        HttpRequest ipv4 = httpRequest.form("ipv4", url);
        String body = ipv4.execute().body();
        JSONObject entries = JSONUtil.parseObj(body);
        // 如果hive限定返回值是一个结构体
        // 处理方法中返回值是一个集合  里面是struct的value
        ArrayList<Object> result = new ArrayList<>();
        result.add(entries.getStr("country"));
        result.add(entries.getStr("province"));
        result.add(entries.getStr("city"));
        result.add(entries.getStr("area"));
        result.add(entries.getStr("isp"));
        return result;
    }

    // 展示内容
    @Override
    public String getDisplayString(String[] children) {
        return getStandardDisplayString("parseIP", children);
    }
}
